채팅방 만들기 실습

✒️ 2025-05-26 14:05 내용 수정



1. 환경 설정

  1. 실습에 사용할 라이브러리를 모두 다운 받는다.
    • express, express-session : express 서버와 세션
    • passport, passport-local : 로그인 기능
    • bcrypt : 암호화 사용
    • dotenv : 환경 설정 파일 사용
    • ejs : view engine
    • method-override : 메소드 오버라이드에 사용하며, HTML에서 PUT을 사용하기 위해 추가
    • mongodb, connect-mongo : MongoDB 연결
    • socket.io : web socket 라이브러리
npm install express express-session ejs bcrypt dotenv passport passport-local socket.io mongodb connect-mongo method-override
{
  "dependencies": {
    "bcrypt": "^5.1.1",
    "connect-mongo": "^5.1.0",
    "dotenv": "^16.4.5",
    "ejs": "^3.1.9",
    "express": "^4.19.2",
    "express-session": "^1.18.0",
    "method-override": "^3.0.0",
    "mongodb": "^6.5.0",
    "passport": "^0.7.0",
    "passport-local": "^1.0.0",
    "socket.io": "^4.7.5"
  }
}
  1. .env 파일을 만들어 실습에서 사용할 port 번호와 MongoDB URL을 설정한다.
# PORT 설정
PORT = 3000

# MongoDB 연결 주소
URL = 'mongoDB URL'
  1. css를 저장할 public 폴더, route 설정을 위한 routes 폴더, ejs 파일들을 저장할 views 폴더를 만든다.

chatroom set.png


2. DB 연결

// 환경 변수
require("dotenv").config();

// mongodb
const { MongoClient } = require("mongodb");

let connectDB = new MongoClient(process.env.URL).connect(); // process.env.URL는 .env에 있는 URL

module.exports = connectDB;
let connectDB = require("../db.js"); // connectDB를 가져옴
let db;

// db 연결
connectDB.then((client) => { // Promise 객체를 resolve 진행
    db = client.db("forum"); // "forum" db 연결
}).catch((err) => {
    console.log(err);
});

3. 서버 사이드

// 서버 세팅 -------------------------------
// 환경변수
require("dotenv").config();

// method-override for REST API
const methodOverride = require("method-override");

// express 설정
const express = require("express");
const app = express();

// http와 socket.io 설정
// https://socket.io/get-started/chat
const { createServer } = require("http");
const { Server } = require("socket.io");
const exp = require("constants");
const server = createServer(app); // express를 사용한 http 서버 생성
const io = new Server(server); // socket 서버 생성

// session 및 로그인 설정
const session = require("express-session"); // session
const passport = require("passport"); // 로그인 
const LocalStrategy = require("passport-local"); // 로그인방법
const bcrypt = require("bcrypt"); // 암호화

// db 설정
const MongoStore = require("connect-mongo"); // DB에 session 저장용
const { ObjectId } = require("mongodb");
// let connectDB = require("./db.js"); // 생성해둔 connection 가져옴
let connectDB = require("./db.js");
let db;

connectDB.then((client) => {
    db = client.db("forum"); // "form" db 연결

    server.listen(process.env.PORT, () => {
        console.log(`서버 접속 가능 : http://localhost:${process.env.PORT}`);
    });
}).catch((err) => {
    console.log(err);
});

// session 설정
const sessionOption = {
    secret : "secret-express-session", // secret 키
    resave : false,
    saveUninitialized : false,
    cookie : { maxAge : 60 * 60 * 1000 }, // 1시간
    store : MongoStore.create({ // db에 세션 저장 설정
        mongoUrl : process.env.URL, // db 주소
        dbName : "forum" // db 이름
    })
}

// app 설정
app.use(express.static(__dirname + "/public"));
app.use(express.json());
app.use(express.urlencoded({ extended : true }));
app.use(methodOverride("_method"));
// method override 사용 | https://expressjs.com/en/resources/middleware/method-override.html
app.set("view engine", "ejs");

// 로그인 설정 - passport | 순서 반드시 지켜야 함
app.use(passport.initialize()); // 초기화, 사용자 인증 처리
app.use(session(sessionOption)); // session 옵션 사용
app.use(passport.session()); // 세션을 사용하도록 설정

passport.use(new LocalStrategy( // 로그인 방법
    {usernameField : "userid", passwordField : "password"}, // 사용자이름, 비번
    async (userid, password, done) => { // 로그인 처리
        // db에서 userid 조회
        let result = await db.collection("user").findOne({ userid });

        // 아이디 없음 처리
        if (!result) {
            return done(null, false, { message : "fail" });
        }

        // 비밀번호 비교 처리
        if (await bcrypt.compare(password, result.password)) {
            return done(null, result);
        } else {
            return done(null, false, { message : "fail" });
        }
    }
));

passport.serializeUser( (user, done) => { // 로그인 시 실행, req.session에 데이터 저장
    process.nextTick(() => {
        done(null, { id : user._id, userid : user.userid });
    });
});

passport.deserializeUser( async (user, done) => { // 매 요청마다 실행, id로 사용자 정보 객체 불러옴
    // id로 사용자 정보 조회
    // user는 passport.serializeUser에서 저장된 user
    let result = await db.collection("user").findOne({ _id : new ObjectId(user.id) });

    delete result.password; // 사용자 정보에서 password 정보 삭제

    process.nextTick(() => {
        return done(null, result); // req.user에 result 저장
    });
});

// socket.io에서 세션 사용-----------------------
const sessionMiddleWare = session(sessionOption);

io.use((socket, next) => {
    sessionMiddleWare(socket.request, {}, next);
});

io.on("connection", (socket) => { // listen on connection event
    console.log("websocket 연결 완료");

    // 채팅방 참여 요청
    socket.on("ask-join", (data) => { // receiver
        socket.join(data);
    });

    socket.on("message-send", async (data) => {
        await db.collection("chatMessage").insertOne({
            parentRoom : new ObjectId(data.room),
            content : data.msg,
            who : new ObjectId(socket.request.session.passport.user.id),
            date : new Date()
        });

        // broadcasting
        io.to(data.room).emit("message-broadcast", data.msg);
    });

});

// 라우터 설정-----------------------------
// 메인 화면
app.get("/", (req, res) => {
    res.redirect("/list");
});

app.use("/", require("./routes/join.js"));
app.use("/", require("./routes/login.js"));
app.use("/", require("./routes/crud.js"));
app.use("/", require("./routes/chat.js"));

4. 라우트

// router 사용
const router = require("express").Router();
const bcrypt = require("bcrypt");
let connectDB = require("../db.js");
let db;

// db 연결
connectDB.then((client) => {
    db = client.db("forum"); // "forum" db 연결
}).catch((err) => {
    console.log(err);
});

// 회원가입 페이지 이동
router.get("/join", (req, res) => {
    res.render("join.ejs", { loginUser : null });
});

// 회원가입
router.post("/join", async (req, res) => {
    const { userid, password } = req.body;
    let hash = await bcrypt.hash(password, 10); // 비밀번호 암호화

	// 사용자 정보를 db에 추가
    let result = await db.collection("user").insertOne({
        userid,
        password : hash
    });

	// 성공 및 실패 여부를 전송
    if (result) {
        res.json({ joinResult : "success"});
    } else {
        res.json({ joinResult : "fail"});
    }
    
});

module.exports = router;
// login.js
// router 사용
const passport = require("passport");
const router = require("express").Router();

// 로그인 페이지 이동
router.get("/login", (req, res) => {
    res.render("login.ejs", { loginUser : null });
});

// 로그인
router.post("/login", async (req, res, next) => {
    passport.authenticate(("local"), (error, user, info) => {
        // 인증 오류 처리
        if (error) return res.json({ message : error });
        // 로그인 실패 처리
        if (!user) return res.json( { message : info.message });
        // 에러 발생 시 next 미들웨어로 오류 처리 넘김
        if (error) return next(err);

        req.logIn(user, (err) => {
            // 에러 발생 시 next 미들웨어로 오류 처리 넘김
            if (err) return next(err);
            return res.json( {message : "success" });
            // res.redirect("/list");
        });
    })(req, res, next);
});

// 로그아웃 - DB에 저장된 세션에도 자동 처리됨
router.get("/logout", (req, res) => {
    req.logOut(() => {
        res.redirect("/");
    });
});

module.exports = router;
// router 사용
const router = require("express").Router();

const { ObjectId } = require("mongodb");
let connectDB = require("../db.js");
let db;

// db 연결
connectDB.then((client) => {
    db = client.db("forum"); // "forum" db 연결
}).catch((err) => {
    console.log(err);
});

// 글 조회
router.get("/list", async (req, res) => {
    const { title } = req.query;
    const loginUser = req.user ? req.user.userid : "unknown";

    // 제목 포함된 글 찾기
    if (title) {
        let result = await db.collection("post").find({
            title : { $regex : new RegExp(title) }
        }).toArray();
        res.render("list.ejs", { list : result });
    } else {
        let result = await db.collection("post").find().toArray();
        res.render("list.ejs", { list : result, loginUser });
    }
});

// 상세보기
router.get("/detail/:id", async (req, res) => {
    const { id } = req.params;
    const loginUser = req.user ? req.user.userid : "unknown";

    try {
	    // DB에서 id로 특정 글 검색
        let result = await db.collection("post").findOne({
            _id : new ObjectId(id)
        });

		// 특정 글의 댓글 검색
        let comment = await db.collection("comment").find({
            parentId : id
        }).toArray();

        if (!result) {
            res.status(400).send("글을 찾을 수 없습니다");
        } else {
            res.render("detail.ejs", { result, comment, loginUser});
        }
    } catch (error) {
        console.log(error);
    }
});

// 글 작성 페이지로 이동
router.get("/add", async (req, res) => {
    // 로그인해야만 글 작성이 가능하므로 req.user에 값이 있는지 확인해야 함
    if (!req.user) {
        return res.redirect("/login");
    }
    res.render("write.ejs", { loginUser : req.user.userid });
});

// 글 작성
router.post("/add", async (req, res) => {
    const { title, content } = req.body;
    const { id, userid } = req.user;

    try {
	    // 글 작성 유효성 검사
        if (title == '') {
            res.send("제목을 입력해주세요");
        } else if (title.length > 20) {
            res.send("제목은 20글자 이하만 가능합니다");
        } else if (content == '') {
            res.send("내용을 입력해주세요");
        } else {
	        // 검사 통과 시 글 추가
            let result = await db.collection("post").insertOne({
                title,
                content,
                userNum : id,
                userid : userid
            }, (error, r) => {
                console.log("글 추가 완료");
            });

            res.redirect("/list");
        }
    } catch (err) {
        console.log(err);
        res.send("글 수정 실패 : DB 에러 발생");
    }
});

// comment 추가
router.post("/comment", async (req, res) => {
    const { content, parentId } = req.body;

    const writerId = req.user.id;
    const writer = req.user.userid;

	// 댓글 추가
    let result = await db.collection("comment").insertOne({
        content,
        parentId,
        writerId,
        writer
    });

    // 이전 페이지로 돌아가기
    res.redirect("back");
});

// 글 수정 페이지 이동
router.get("/edit/:id", async (req, res) => {
    const { id } = req.params;
    const loginUser = req.user ? req.user.userid : null;
    let result;

    if (loginUser) { // 로그인 진행때만 수행. 작성자와 로그인한 계정 검사는 추가 못함
        result = await db.collection("post").findOne({
            _id : new ObjectId(id)
        });
    } else {
        res.redirect("/login");
    }

    res.render("edit.ejs", { result, loginUser });
});

// 글 수정
router.put("/edit", async (req, res) => {
    const { id, title, content } = req.body;

    try {
	    // 사용자 로그인 여부 및 작성자와 일치 여부는 추가 못함
	    // 글 수정 진행
        let result = await db.collection("post").updateOne(
            { _id : new ObjectId(id) }, 
            { $set : { title, content } }
        );
        res.redirect("/list");
    } catch (error) {
        console.log(error);
    }
});

// 글 삭제
router.delete("/delete/:id", async (req, res) => {
    const post_id = req.params.id;
    const userNum = req.user.id;

    try {
	    // 글 삭제 진행
        let result = await db.collection("post").deleteOne({
            _id : new ObjectId(post_id),
            userNum : userNum
        });

        const { deleteCount } = result;

        if (deleteCount == 0) {
            res.send("글 삭제 실패");
        }

		// list.ejs에서 fetch로 delete를 요청했기에 redirect 수행 x
        res.send("제거 완료");
    } catch (error) {
        console.log(error);
    }
});

module.exports = router;
// router 사용
const router = require("express").Router();
const bcrypt = require("bcrypt");

const { ObjectId } = require("mongodb");
let connectDB = require("../db.js");
let db;

// db 연결
connectDB.then((client) => {
    db = client.db("forum"); // "forum" db 연결
}).catch((err) => {
    console.log(err);
});

// 채팅방 조회
router.get("/chat/list", async (req, res) => {
    const loginUser = req.user ? req.user.userid : null;
    const loginUserid = req.user ? req.user.id : null;

    if (req.user) { // 로그인 시에만 채팅방 목록 조회
        let result = await db.collection("chatroom").find({
	        // 채팅방 참여자에 로그인한 사용자 정보가 있는 경우를 조건으로 추가
            member : { $in : [
                loginUserid,
                new ObjectId(loginUserid),
            ]}
        }).toArray();

        res.render("chatList.ejs", { result, loginUser });
    } else {
        res.redirect("/login");
    }
});

// 채팅 요청
router.get("/chat/request", async (req, res) => {
    const loginUserid = req.user ? req.user.id : null;

    // 글 작성자와 현재 로그인한 유저가 속한 채팅방 조회
    let result = await db.collection("chatroom").find({
        member : { $all : [loginUserid, new ObjectId(req.query.writerId)]}
    }).toArray();

    if (result.length == 0) { // 채팅방이 없다면 새로 추가
        await db.collection("chatroom").insertOne({
            member : [loginUserid, new ObjectId(req.query.writerId)],
            date : new Date()
        });
    }

    res.redirect("/chat/list");
});

// 채팅방 내용 확인
router.get("/chat/detail/:id", async (req, res) => {
    const { id } = req.params;
    const loginUser = req.user ? req.user.userid : null;
    const loginUserid = req.user ? req.user.id : null;

    if (loginUser) { // 로그인 시에만 채팅 내역 확인 가능
        // 채팅방 조회
        let result = await db.collection("chatroom")
        .findOne({ _id : new ObjectId(id) });

        // 채팅 참여자 조회
        let member = await db.collection("user")
        .find({ _id : { $in : [
            new ObjectId(result.member[0]),
            new ObjectId(result.member[1].toString()),
        ]}}).toArray();
        
        member.map((el) => { // 조회한 사용자 정보 중 비밀번호는 제거함
            return delete el.password;
        });

        // 채팅 참여 인원 외의 다른 사람 접근 차단
        if (loginUserid != member[0]._id.toString() && loginUserid != member[1]._id.toString() ) {
            return;
        }

        // 채팅 내역 조회
        let chat = await db.collection("chatMessage")
        .find({ parentRoom : new ObjectId(id) }).toArray();

        res.render("chatDetail.ejs", { result, chat, member, loginUser, loginUserid });
    } else {
        res.redirect("/login");
    }

});

module.exports = router;

5. views(ejs)

<header class="header">
  <div class="inner">
    <h1><a class="logo" href="/">Forum</a></h1>
    <nav class="nav">
      <% if (!loginUser || loginUser == "unknown") { %>
      <a href="/join">join</a>
      <a href="/login">login</a>
      <% } else { %>
      <span class="username">welcome! <%= loginUser %></span>
      <a href="/logout">logout</a>
      <% } %>
      <a href="/add">write</a>
      <a href="/chat/list">chatroom</a>
    </nav>
  </div>
</header>

chatroom view 1.png
chatroom view 2.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>register</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('nav.ejs') %>

  <section class="sec">
    <h2>회원가입</h2>
    <form class="form-box">
      <input name="userid" placeholder="userid" value="">
      <input name="password" placeholder="password" type="password" value="">
      <button class="confirm">확인</button>
    </form> 
  </section>

  <!-- ajax를 쉽게 사용하기 위해 jquery를 사용했다 -->
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script>
    const $form = $(".form-box");

    $(".confirm").on("click", function (event) {
      event.preventDefault(); // form 제출 방지. 원래는 form을 사용했다가 변경했다
      
      let $userid = $("input[name='userid']").val();
      let $password = $("input[name='password']").val();

	  // 유효성 검사
      if(!$userid) {
        alert("아이디를 입력해주세요");
        return;
      } else if (!$password) {
        alert("비밀번호를 입력해주세요");
        return;
      }

	  // ajax로 데이터를 전송하고 그 결과로 alert 창을 띄움
      $.ajax({
        url : "/join",
        type : "POST",
        data : JSON.stringify({'userid' : $userid, 'password' : $password}),
        dataType : "json",
        contentType : "application/json; charset=utf-8",
        success: function(data) {
          if(data["joinResult"]) {
            alert("성공적으로 회원가입 되었습니다!");
            window.location.href = "/login";
          } else {
            alert("회원가입을 실패했습니다. 다시 시도해주세요");
            return;
          }
        }
      });
    })
    
  </script>
</body>
</html>

chatroom view 3.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>login</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('nav.ejs') %>

  <section class="sec">
    <h2>로그인</h2>
    <form class="form-box">
      <input name="userid" placeholder="userid" value="">
      <input name="password" placeholder="password" type="password" value="">
      <button class="confirm">확인</button>
      <div class="error-box"></div>
    </form> 
  </section>

  <!-- ajax를 쉽게 사용하기 위해 jquery를 사용했다 -->
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script>
    const $form = $(".form-box");
    let $error = $(".error-box");
    const message = "<p>아이디가 존재하지 않거나 비밀번호가 일치하지 않습니다.<br>아이디와 비밀번호를 다시 확인해주세요</p>";

    $(".confirm").on("click", function (event) {
      event.preventDefault();
      
      let $userid = $("input[name='userid']").val();
      let $password = $("input[name='password']").val();

	  // 유효성 검사
      if(!$userid) {
        alert("아이디를 입력해주세요");
        return;
      } else if (!$password) {
        alert("비밀번호를 입력해주세요");
        return;
      }

	// ajax
      $.ajax({
        url : "/login",
        type : "POST",
        data : JSON.stringify({'userid' : $userid, 'password' : $password}),
        dataType : "json",
        contentType : "application/json; charset=utf-8",
        success : function (data) {
          console.log(data)
          if (data["message"] == "success") {
            window.location.href ="/";
            $error.removeClass("fail");
          } else {
            $error.addClass("fail");
            $(".fail").html(message);
            return;
          }
        }
      });
    })
    
  </script>
</body>
</html>

chatroom view 4.png
chatroom view 5.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>list</title>
  <link rel="stylesheet" href="/style.css">

</head>
<body>
  
  <%- include('nav.ejs') %>
  
  <section class="sec">

    <h2> 글 목록 </h2>

    <div class="white-bg">
      <% if ( list.length == 0 ) {%>
        <p> 새로운 글을 작성해주세요. 👉 <a href="/add" style="list-style:underline">글쓰기</a></p>
      <% } else { %>

      <% for(let i = 0; i < list.length; i++) { %>
        <% let checkLogin = loginUser == list[i].userid %>
        <% let className = (checkLogin)? 'writer login' : 'writer' %>
        <div class="list-box">
          <a href="/detail/<%= list[i]._id %>">
            <h3><%= list[i].title %></h3>
            <p><%= list[i].content %></p>
            <p class="<%= className %>"><%= list[i].userid %></p>
          </a>
        <% if(checkLogin) { %>
          <div class="btn">
            <a href="/edit/<%= list[i]._id %>">✏️</a>
            <a href="/delete/<%= list[i]._id %>" class="delete" data-id="<%= list[i]._id %>" >🗑️</a>
          </div>
        <% } %>
          
        </div>

      <% } } %>
    </div>
  </section>

  <script>
    const BASE_URL = 'http://localhost:3000'
    const del = document.querySelectorAll('.delete');

    del.forEach((el, i)=>{

      el.addEventListener('click', async function(e){
          
        e.preventDefault();
        const id = e.target.dataset.id

        // 새로고침을 안하고 동작
        const response = await fetch(`${BASE_URL}/delete/${id}`, {
          method: 'DELETE',
        });
        console.log(response)
        e.target.parentElement.parentElement.style.display = 'none'
      })
    })
    
  </script>

</body>
</html>

chatroom view 6.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>write</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('nav.ejs') %>

  <section class="sec">
    <h2>글 쓰기</h2>
    <form class="form-box" action="/add" method="POST">
      <input name="title" placeholder="title">
      <input name="content" placeholder="content">
      <button>전송</button>
    </form> 
  </section>
</body>
</html>

chatroom view 7.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>post</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('nav.ejs') %>
  <section class="sec">
    <div class="white-bg">
      <div class="list-box">
        <h3><%= result.title %></h3>
        <p class="writer">
          작성자 : <%= result.userid %> 
          <% let checkLogin = loginUser == result.userid %>
          <% if(checkLogin) { %>
            <span class="btn">
              <a href="/edit/<%= result._id %>">✏️</a>
              <a href="/delete/<%= result._id %>" class="delete" data-id="<%= result._id %>" >🗑️</a>
            </span>
          <% } %>
        </p>
        <!-- writerId : 작성자 id, reslut._id : 현재 로그인한 사용자 아이디 -->
        <% if(!checkLogin) { %>
        <a href="/chat/request?writerId=<%= result._id %>">채팅하기</a>
        <% } %>
        <p><%= result.content  %></p>
      </div>

      <!-- parentId : 게시글번호 (post컬렉션의 _id)-->
      <div class="comment-write">
      <% if(loginUser == 'unknown'){ %>
        <a href="/login">로그인</a> 해주세요
      <% }else{ %>
        <form action="/comment" method="POST" id="comment">
          <label for="commentContent"><%= loginUser %> </label>
          <input name="content" id="commentContent">
          <input name="parentId" value="<%= result._id %>" type="hidden">
          <button>댓글작성</button>
        </form>
      <% } %>
      </div>

      <!-- 댓글목록 -->
      <div class="comment-list">
          <% for(let i = 0; i < comment.length; i++){ %>
            <p>
              <span><%= comment[i].writer %> </span>
              <span><%= comment[i].content %></span>
            </p>
          <% } %>
      </div>
    </div>
  </section>

</body>
</html>

chatroom view 8.png
chatroom view 9.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>write</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <%- include('nav.ejs') %>

  <section class="sec">
    <h2>글수정</h2>
    <!-- https://expressjs.com/en/resources/middleware/method-override.html -->
    <form class="form-box" action="/edit?_method=PUT" method="POST">
      <input class="inputId" name="id" value="<%= result._id%>" type="hidden">
      <input class="inputTitle" name="title" placeholder="title" value="<%= result.title%>">
      <input class="inputContent" name="content" placeholder="content" value="<%= result.content%>">
      <button class="edit">전송</button>
    </form> 
  </section>

</body>
</html>

chatroom view 10.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>list</title>
  <link rel="stylesheet" href="/style.css">

</head>
<body>
  
  <%- include('nav.ejs') %>
  
  <section class="sec"> 
    <h2> 채팅 목록 </h2>
    <div class="white-bg">
      <% for (let i = 0; i < result.length; i++){ %>
        <div class="list-box">
          <!-- id : chatroom의 _id 고유번호 -->
          <a href="/chat/detail/<%= result[i]._id%>">
            <h3><%= result[i].member[0]%></h3>
            <p><%= result[i]._id%></p>
            <p>test</p>
          </a>
        </div>
      <% } %>
  </div> 
  </section>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>chatroom</title>
  <link rel="stylesheet" href="/style.css">

</head>
<body>
  <%- include('nav.ejs') %>
  <!-- 
    member[0] : 현재 로그인 아이디
    member[1] : 글 작성자 아이디
  -->

  <section class="sec">
    <div class="detail-bg">
      <div class="title">
        <h2><%= member[0].userid%>님과 <%= member[1].userid%>님의 채팅룸</h2>
      </div>
      <div class="chat-screen">
        <% for( let i = 0; i < chat.length ; i++) {
            let chatDate = chat[i].date.toLocaleDateString("ko-kr");
            let prevDate;
            if (i > 0) {
              prevDate = chat[i-1].date.toLocaleDateString("ko-kr");
            }
            if (i == 0 || chatDate != prevDate) {
          %>
          <h3 class="chat-date"><%= chatDate %></h3>
          <% } %>

          <% if (chat[i].who.toString() == loginUserid) { %>
          <div class="chat-box mine">
          <% } else { %>
          <div class="chat-box">
          <% } %>
            <span><%=chat[i].date.toLocaleTimeString("ko-kr", {hour : "2-digit", minute : "2-digit"}) %></span>
            <span><%=chat[i].content %></span>
          </div>
        <% } %>
      </div>
    </div>
    <div class="chat-form">
      <input class="chat-input">
      <button class="chat-button">전송</button>
    </div> 
  </section>

  <script src="https://cdn.jsdelivr.net/npm/socket.io@4.7.2/client-dist/socket.io.min.js"></script>
  <script>
    // socket.io-client 로드를 위한 snippet 추가
    const socket = io();

    socket.emit('ask-join', '<%= result._id %>');

    document.querySelector('.chat-button').addEventListener('click', function () {
      let msg = document.querySelector('.chat-input').value;
      // main namespace에 event 방출 : 메시지 전송
      socket.emit('message-send', {room: '<%= result._id %>', msg });
      document.querySelector('.chat-input').value = null;
    })

    socket.on('message-broadcast', (data) => {
      document.querySelector('.chat-screen')
        .insertAdjacentHTML('beforeend', 
        `<div class="chat-box mine">
          <span><%=new Date().toLocaleTimeString("ko-kr", {hour : "2-digit", minute : "2-digit"}) %></span>
          <span>${data}</span>
        </div>`
        )
    });

  </script>
</body>
</html>